﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace P2P.Main.Util
{
    public class SnowflakeIdGenerator
    {
        private const long Twepoch = 1288834974657L;
        private const int WorkerIdBits = 10;
        private const int DatacenterIdBits = 10;
        private const int SequenceBits = 12;

        private long workerId;
        private long datacenterId;
        private long sequence = 0L;

        private long lastTimestamp = -1L;

        private static readonly object _lock = new object();

        private SnowflakeIdGenerator()
        {
           
        }

        private static SnowflakeIdGenerator _instance;

        public static SnowflakeIdGenerator Instance 
        {
            get 
            {
                if (_instance == null) 
                {
                    lock (_lock) 
                    {
                        if (_instance == null)
                        {
                            _instance = new SnowflakeIdGenerator();
                        }
                    }
                }
                return _instance;
            }
        }

        public void Init(long workerId,long datacenterId) 
        {
            workerId = workerId;
            datacenterId = datacenterId;
        }

        public long NextId()
        {
            long timestamp = TimeGen();

            if (timestamp < lastTimestamp)
            {
                throw new Exception("时钟回拨");
            }

            if (lastTimestamp == timestamp)
            {
                sequence = (sequence + 1) & ((1 << SequenceBits) - 1);
                if (sequence == 0)
                {
                    timestamp = TilNextMillis(lastTimestamp);
                }
            }
            else
            {
                sequence = 0L;
            }

            lastTimestamp = timestamp;

            long id = ((timestamp - Twepoch) << (WorkerIdBits + DatacenterIdBits + SequenceBits))
                | (datacenterId << (WorkerIdBits + SequenceBits))
                | (workerId << SequenceBits)
                | sequence;

            // 确保 ID 为正数
            if (id < 0)
            {
                id = -id;
            }

            return id;
        }

        private long TilNextMillis(long lastTimestamp)
        {
            long timestamp = TimeGen();
            while (timestamp <= lastTimestamp)
            {
                timestamp = TimeGen();
            }
            return timestamp;
        }

        private long TimeGen()
        {
            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
        }
    }
}
